/*
 *  $Id: ParticleEmitter.cs 1133 2010-09-29 15:42:17Z thenn.erannor $
 *	This file is a part of Ragima CCG.
 *	(C) 2008-2009 Ragima development group.
 *
 *	Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System.Collections.Generic;

using AgateLib.Geometry;

namespace Ragima.UI {
    /// <summary>
    /// Эмиттер частиц.
    /// </summary>
    public class ParticleEmitter {
        private IEnumerator<double> _rate;
        private IEnumerator<Particle> _generator;
        private IEnumerator<PointF> _trajectory;
        private List<IParticleEffect> _effects;
        private double _rateAccum;
        private PointF _offset;
        private int _duration, _time;
        private bool _ended;

        private List<Particle> _particles;

        public ParticleEmitter(ParticleEmitterInfo info) : this(info.Duration, info.Rate, info.Generator, info.Trajectory, info.Effects) {}
        public ParticleEmitter(int duration, IEnumerable<double> rate, IEnumerable<Particle> generator, IEnumerable<PointF> trajectory, params IParticleEffect[] effects) : this(duration, rate, generator, trajectory, effects as IEnumerable<IParticleEffect>) {}
        public ParticleEmitter(int duration, IEnumerable<double> rate, IEnumerable<Particle> generator, IEnumerable<PointF> trajectory, IEnumerable<IParticleEffect> effects) {
            _duration = duration;
            _rate = rate != null ? rate.GetEnumerator() : null;
            _generator = generator != null ? generator.GetEnumerator() : null;
            _trajectory = trajectory != null ? trajectory.GetEnumerator() : null;
            _effects = effects != null ? new List<IParticleEffect>(effects) : null;
            _time = 0;
            _particles = new List<Particle>();
            _rateAccum = 1;
            _offset = PointF.Empty;
            _ended = false;
        }

        public bool Ended {
            get { return _ended && _particles.Count == 0; }
            set { _ended = value; }
        }

        public void Draw() {
            for (int i = 0; i < _particles.Count; i++)
                _particles[i].Draw();
        }

        public void Process() {
            // обработка траектории
            UpdateOffset();
            // обработка частиц
            for (int i = 0; i < _particles.Count; i++) {
                Particle part = _particles[i];
                part.Process();
                if (_effects != null)
                    for (int e = 0; e < _effects.Count; e++)
                        _effects[e].DoEffect(part);
            }
            // удаление умерших частиц
            _particles.RemoveAll(delegate(Particle part) { return part.Ended; });
            // добавление новых частиц
            if (!_ended) {
                _rateAccum += GetRate();
                while (_rateAccum >= 1) {
                    Particle part = GetParticle();
                    if (part != null) {
                        part.MoveBy(_offset);
                        _particles.Add(part);
                    }
                    _rateAccum -= 1;
                }
            }
            // счетчик времени
            _time++;
            // проверка на окончание
            if (_duration > 0 && _time >= _duration)
                _ended = true;
        }

        private double GetRate() {
            if (_rate == null)
                return 0;
            if (_rate.MoveNext()) {
                return _rate.Current;
            } else {
                _rate.Dispose();
                _rate = null;
                return 0;
            }
        }

        private Particle GetParticle() {
            if (_generator == null)
                return null;
            if (_generator.MoveNext()) {
                return _generator.Current;
            } else {
                _generator.Dispose();
                _generator = null;
                return null;
            }
        }

        private void UpdateOffset() {
            if (_trajectory == null)
                return;
            if (_trajectory.MoveNext()) {
                _offset = _trajectory.Current;
            } else {
                _trajectory.Dispose();
                _trajectory = null;
            }
        }
    }
}
